<!DOCTYPE html>
<html>

<head>
  <title>Volcano</title>
  <script src="d3.min.js"></script>
</head>

<body>
  <svg width="1600" height="800" id="mainsvg" class="svgs" style='display: block; margin: 0 auto;'></svg>
  <script>
      // The following code is the typical routine of my d3.js code. 
      const svg = d3.select('svg');
      const width = svg.attr('width');
      const height = svg.attr('height');
      const margin = {top: 60, right: 30, bottom: 60, left: 150};
      const innerWidth = width - margin.left - margin.right;
      const innerHeight = height - margin.top - margin.bottom;
      const mainGroup = svg.append('g')
      .attr('transform', `translate(${margin.left}, ${margin.top})`);

      let thresholds; 
      let color = d3.scaleSequential(d3.interpolatePlasma);
      let volcanoData, geoPolygons, geoPolygonsPre, geoPolygonsPost;
      const contours = d3.contours();
      const projection = d3.geoNaturalEarth1();
      const path = d3.geoPath().projection(projection);
      const geoJson = {"type": "FeatureCollection", "features": []};

      const render = function(gp){
          svg.selectAll('.myPath').data(gp, d => d.id).join('path')
          .attr('class', 'myPath')
          .attr("fill", d => color(d.value))
          .attr("stroke", "white")
          .attr("stroke-width", 0.03)
          .transition().duration(2000)
          .attr('d', d => path(d));
      }

      const renderT = function(){
          let t = ( performance.now() / 2000 ) % 1;
          return svg.selectAll('.myPath')
          .attr("fill", d => color(d[0] * (1-t) + d[1] * t))
          .attr('d', d => path(contours.contour(volcanoData.values, (d[0] * (1-t) + d[1] * t))));
      }

      d3.json('volcano.json').then(async data => {
          volcanoData = data;
          color.domain(d3.extent(data.values));
          thresholds = color.ticks(20); 
          contours.size([data.width, data.height]).thresholds(thresholds).smooth(true);
          
          // 构造GeoJson并FitSize。
          for (const threshold of thresholds) {
              // Computes a single contour, returning a GeoJSON MultiPolygon geometry object. 
              geoJson.features.push({'geometry': contours.contour(data.values, threshold)});
          }
          projection.fitSize([width, height], geoJson);
          
          
          geoPolygons = contours(data.values);

          let thresholdPairs = d3.pairs(thresholds);
          contours.thresholds(thresholdPairs.map(d => d[0]));
          geoPolygonsPre = contours(data.values);
          contours.thresholds(thresholdPairs.map(d => d[1]));
          geoPolygonsPost = contours(data.values);

          for(let i = 0; i < geoPolygonsPre.length; i++){
              geoPolygonsPre[i].id = i;
              geoPolygonsPost[i].id = i;
          }
          
          // render(geoPolygonsPre);
          // render(geoPolygonsPost);

          svg.selectAll('.myPath').data(thresholdPairs).join('path')
          .attr('class', 'myPath')
          //.attr("fill", d => color(d[0]))
          .attr("stroke", "white")
          .attr("stroke-width", 1)
          //.attr('d', d => path(contours.contour(volcanoData.values, d[0])))

          const total = 2000;
          const freq = 200;
          const interval = total / freq;
          let current = 0;
          const scaleInter = d3.scaleLinear().domain([0, total]).range([0, 1]);
          while(true){
              current = 0; 
              while( current < total ){
                  const t = scaleInter(current);
                  let transition = d3.transition().duration(interval);
                  svg.selectAll('.myPath')
                  .attr('d', d => path(contours.contour(volcanoData.values, (d[0] * (1-t) + d[1] * t))))
                  .transition(transition)
                  .attr("fill", d => color(d[0] * (1-t) + d[1] * t));              
                  await transition.end();
                  current += interval;
              }
          }

          //renderT();
      })

  </script> 
</body>

</html>